Nestjs 使用 graphql
基本使用
安装依赖
bash1pnpm i @nestjs/graphql @nestjs/apollo @apollo/server graphql
在 AppModule 中引入
typescript1import { Module } from '@nestjs/common' 2import { AppController } from './app.controller' 3import { AppService } from './app.service' 4import { GraphQLModule } from '@nestjs/graphql' 5import { ApolloDriver } from '@nestjs/apollo' 6import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default' 7 8@Module({ 9 imports: [ 10 GraphQLModule.forRoot({ 11 driver: ApolloDriver, 12 playground: false, 13 plugins: [ApolloServerPluginLandingPageLocalDefault()], 14 typePaths: ['./**/*.graphql'], 15 }), 16 ], 17 controllers: [AppController], 18 providers: [AppService], 19}) 20export class AppModule {}
- driver:使用 ApolloDriver 作为驱动程序
- playground:是否使用 Graphql-playground,这里禁用掉,因为 Apollo-sandbox 更好用
- plugins:插件,使用
ApolloServerPluginLandingPageLocalDefault开启 Apollo-sandbox - typePaths :编写的 GraphQL Schema 文件的路径
创建 Resource
bash1nest g resource --no-spec 2? What name would you like to use for this resource (plural, e.g., 3"users")? users 4? What transport layer do you use? GraphQL (schema first) 5? Would you like to generate CRUD entry points? Yes
resource 能够生成一个比较完整的 CRUD 示例,包含 graphql schema、CRUD resolver 等等。
生成后会创建这些文件。
bash1CREATE src/users/users.graphql (397 bytes) 2CREATE src/users/users.module.ts (225 bytes) 3CREATE src/users/users.resolver.ts (959 bytes) 4CREATE src/users/users.service.ts (625 bytes) 5CREATE src/users/dto/create-user.input.ts (32 bytes) 6CREATE src/users/dto/update-user.input.ts (192 bytes) 7CREATE src/users/entities/user.entity.ts (21 bytes)
更新 app.module.ts把 UsersModule 引入进来。
diff1imports: [ 2 GraphQLModule.forRoot(...), 3+ UserModule, 4 ],
自动生成 type 的脚本
安装 ts-morph
bash1pnpm install ts-morph
在根目录创建gen-typings.ts文件并写入:
typescript1import { GraphQLDefinitionsFactory } from '@nestjs/graphql' 2import { join } from 'path' 3 4const definitionsFactory = new GraphQLDefinitionsFactory() 5definitionsFactory.generate({ 6 typePaths: ['./src/**/*.graphql'], 7 path: join(process.cwd(), 'src/graphql.ts'), 8})
在 package.json 中的 scripts 加上:
json1"gen-typings": "ts-node gen-typings.ts"
执行 pnpm run gen-typings将生成类型文件。

进入http://localhost:3000/graphql用 Apollo-sandbox 查询:

传递额外的 context
每个 resolver 都会收到的 context 是 req ,如果想要额外添加则可以在GraphQLModule.forRoot中传递:
typescript1 GraphQLModule.forRoot({ 2 ... 3 context: () => ({ data: '123' }), 4 }),
在 Query 中通过 @Context装饰器拿到它并打印出来:
typescript1 @Query('users') 2 findAll(@Context() ctx: any) { 3 console.log('——————🚀🚀🚀🚀🚀 —— findAll —— ctx.data:', ctx.data); 4 5 return this.usersService.findAll(); 6 }
directive
定义指令标识符
scheme1// directive.graphql 2directive @upper on FIELD_DEFINITION 3directive @auth on FIELD_DEFINITION
定义指令 transformer:
示例 1:将 string 结果大写
typescript1// upper.transformer.ts 2import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils' 3import { defaultFieldResolver, GraphQLSchema } from 'graphql' 4 5export function upperDirectiveTransformer(schema: GraphQLSchema, directiveName: string) { 6 return mapSchema(schema, { 7 [MapperKind.OBJECT_FIELD]: (fieldConfig) => { 8 const upperDirective = getDirective(schema, fieldConfig, directiveName)?.[0] 9 10 if (upperDirective) { 11 const { resolve = defaultFieldResolver } = fieldConfig 12 13 fieldConfig.resolve = async function (source, args, context, info) { 14 const result = await resolve(source, args, context, info) 15 if (typeof result === 'string') { 16 return result.toUpperCase() 17 } 18 return result 19 } 20 return fieldConfig 21 } 22 }, 23 }) 24}
示例 2:传递 auth 给 context
typescript1import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils' 2import { defaultFieldResolver, GraphQLSchema } from 'graphql' 3 4export function authDirectiveTransformer(schema: GraphQLSchema, directiveName: string) { 5 return mapSchema(schema, { 6 [MapperKind.OBJECT_FIELD]: (fieldConfig) => { 7 const authDirective = getDirective(schema, fieldConfig, directiveName)?.[0] 8 9 if (authDirective) { 10 const { resolve = defaultFieldResolver } = fieldConfig 11 12 fieldConfig.resolve = async function (source, args, context, info) { 13 const result = await resolve(source, args, { ...context, auth: 'auth' }, info) 14 15 return result 16 } 17 return fieldConfig 18 } 19 }, 20 }) 21}
GraphQLModule.forRoot中装载:
typescript1 GraphQLModule.forRoot({ 2 ... 3 context: () => ({ data: '123' }), 4 transformSchema: (originalSchema) => { 5 let schema = originalSchema; 6 7 schema = upperDirectiveTransformer(schema, 'upper'); 8 schema = authDirectiveTransformer(schema, 'auth'); 9 10 return schema; 11 }, 12 }),
新建一个 Query 查询:
typescript1type Query { 2 userName: String! @upper @auth 3}
在 resolver 中写处理方法:
typescript1 @Query('userName') 2 async userName(@Context() ctx: any): Promise<string> { 3 console.log('——————🚀🚀🚀🚀🚀 —— getUserName —— Context:', ctx); 4 return ctx.auth; 5 }
用 Apollo-sandbox 查询一下:

指令成功运行,拿到 context 并且将结果大写了。